package pl.szpadel.android.gadu.packets;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ReadableByteChannel;

import pl.szpadel.android.gadu.AGLog;
import pl.szpadel.android.gadu.CommunicationService;

/** Commn class for received packages. Provides createFromStream() */
public abstract class ReceivedPacket extends BasePacket {
	
	static private final String TAG = "ReceivedPacket";
	
	/// Max allowed packet type (excluding header)
	private static final int MAX_PACKET_SIZE = 65536; // 64 kb
	
	/// shared buffer used to read data
	private static ByteBuffer mBuffer = ByteBuffer.allocateDirect(MAX_PACKET_SIZE);
	
	/// Reimplementable - reads content form buffer
	protected abstract void readFromBuffer(ByteBuffer buffer);
	
	
	
	/// protected constructor - initlaizes package from header and data stream
	// TODO remove if not used
	protected ReceivedPacket(Header hdr, InputStream stream) throws IOException {
		super(hdr.getType());
		ByteBuffer buffer = ByteBuffer.allocate(hdr.getLength());
		buffer.order(ByteOrder.LITTLE_ENDIAN);
		if (stream.read(buffer.array()) != hdr.getLength()){
			throw new IOException("failed to read packet data");
		}
		readFromBuffer(buffer);
	}
	
	/// Creates packet from channel
	protected ReceivedPacket(Header hdr, ReadableByteChannel channel) throws IOException {
		super(hdr.getType());
		
		if (hdr.getLength() > MAX_PACKET_SIZE){
			throw new IOException("Packet too large (" + hdr.getLength() + " bytes, type=0x "+ Integer.toHexString(hdr.getType()) +")");
		}
		
		mBuffer.clear();
		mBuffer.limit(hdr.getLength());
		while(mBuffer.position() < hdr.getLength()){
			int r = channel.read(mBuffer);
			if (r < 0){
				throw new IOException("Channel closed while reading packet");
			} else if (r == 0) {
				AGLog.e(CommunicationService.TAG, "received packet: read 0 bytes!!!");
				throw new IOException("channel not ready for reading!");
			}
		}

		mBuffer.flip();
		mBuffer.order(ByteOrder.LITTLE_ENDIAN);
		readFromBuffer(mBuffer);
	}
	
	// creates package from channel
	public static ReceivedPacket createFromChannel(ReadableByteChannel channel) throws IOException {
		AGLog.d(TAG, "Creating header data...");
		Header hdr = Header.createFromChannel(channel);
		AGLog.d(TAG, "Creating packet from channel, hdr: " + hdr);
		
		// factory
		switch(hdr.getType()){
		case TYPE_WELCOME:
			return new Welcome(hdr, channel);
		case TYPE_NOTIFY_REPLY80:
			return new NotifyReply80(hdr, channel);
		case TYPE_STATUS80:
			return new NotifyReply80(hdr, channel); // yes, this is the same stuff
		case TYPE_RECV_MSG80:
			return new RecvMsg80(hdr, channel);
		case TYPE_USERLIST_REPLY80:
			return new UserlistReply80(hdr, channel);
		case TYPE_SEND_MSG_ACK:
			return new SendMsgAck(hdr, channel);
		default:
			return new UnknownReceivedPacket(hdr, channel);
		}
	}
	
	/// Reads string from buffer using gg convention - firt 32 int with len, then utf-8 data
	protected static String getString(ByteBuffer buffer) {
		int len = buffer.getInt();
		if (len > 0 ){
			byte[] data = new byte[len];
			buffer.get(data, 0, len);
			
			try {
				return new String(data, "UTF-8");
			} catch (UnsupportedEncodingException e) {
				return ""; // it doesn't matter really
			}
		} else {
			return "";
		}
	}
		
	
}
